#include <fstream>

#include "stegit_framework.h"
#include "types.h"
#include "engine_factory.h"

#include "file/simplefile_protocol_engine.h"
#include "file/file_protocol_engine.h"
#include "gui_protocol_engine.h"
#include "dummy_protocol_engine.h"

#include "lsb/lsb_embedding_engine.h"
#include "lsb/dump_embedding_engine.h"
#include "lsb/rand_embedding_engine.h"
#include "crypto/crypto_engine.h"
#include "crypto/crypto_engines.h"
#include "crypto/null_crypto_engine.h"
#include "crypto/amplitude_scrambling.h"
#include "crypto/adv_amplitude_scrambling.h"
#include "crypto/frequency_scrambling.h"

#include "byte_builder.h"
#include "frame_builder.h"
#include "packet_builder.h"
#include "stream_builder.h"
#include "datagram_builder.h"
#include "dummy_builder.h"

#include "logger.h"

// meta configuration file for the stegit-framework, loads other configuration files
const std::string CEngineFactory::sDefaultConfigFilename = "/etc/stegit-2/stegit-2.conf";
const std::string CEngineFactory::sIniSectionName = "ENGINE_FACTORY";

// filenames for dump_protocol_engine
const std::string CEngineFactory::sSendOriginalFilename = "/tmp/send_org.dat";
const std::string CEngineFactory::sSendModifiedFilename = "/tmp/send_mod.dat";
const std::string CEngineFactory::sRecvModifiedFilename = "/tmp/recv_mod.dat";

CStegCallHandle* CEngineFactory::createCallHandle
(
		const std::string& pRemoteID,
		CPropertiesReader* pConfig,
		NAudioFormats::SStreamInfo pIoStreamInfo
)
{
	return createCallHandle_(pRemoteID,
			pConfig,
			pIoStreamInfo);
}

void CEngineFactory::destroyCallHandle(CStegCallHandle** pCallHandle)
{
#ifdef DEBUG
	printl(0, __FILE__,
			"CEngineFactory::destroyCallHandle() | CallHandle(%p) \n",
			*pCallHandle);
#endif

	if((*pCallHandle) != NULL)
	{
		destroyCallHandle_(pCallHandle);
	}

	cleanup_log();
}

CStegCallHandle* CEngineFactory::createCallHandle_(const std::string& pRemoteID,
		CPropertiesReader* pConfig,
		NAudioFormats::SStreamInfo pIoStreamInfo)
{
#ifdef DEBUG
	printl(0, "engine_factory.cxx",
			"CEngineFactory::createCallHandle() | RemoteID(%s)\n",
			pRemoteID.c_str());
#endif

	CStegCallHandle* lHandle = new CStegCallHandle();

	if(pConfig) {
		lHandle->mPropertiesReader = pConfig;
		lHandle->mUseExternPropertiesReader = true;
	}
	else {
		// properties reader object for the global configuration file
		lHandle->mPropertiesReader = new CPropertiesReader(sDefaultConfigFilename);
		lHandle->mUseExternPropertiesReader = false;
	}

	std::string lString;
	bool lUseProtocol(true);
	bool lUseBitSource(true);
	if (pConfig->getValue(sIniSectionName, "EMBEDDING_ENGINE", lString) &&
			(lString == "DEFENSE" || lString == "CRYPTO")) {
		lUseProtocol = false;
		lUseBitSource = false;
	}
	/* ======================== ProtocolEngine ======================== */
  if (lUseProtocol && pConfig->containsKey(sIniSectionName, "PROTOCOL_ENGINE"))
  {
    if (pConfig->getValue(sIniSectionName, "PROTOCOL_ENGINE", lString))
    {   
      if (lString == "SIMPLEFILE")
      {   
				lHandle->mProtocolEngine = new CSimplefileProtocolEngine(lHandle->mPropertiesReader);
      }   
      else if (lString == "GUI")
      {   
        lHandle->mProtocolEngine = new CGuiProtocolEngine(lHandle->mPropertiesReader);
      }   
      else /* lString == "FILE" || lString == "DEFAULT" */
      {   
        lHandle->mProtocolEngine = new CFileProtocolEngine(lHandle->mPropertiesReader);
      }   
    }   
  }

	/* ======================== BitSource ======================== */
  if (lUseBitSource && pConfig->containsKey(sIniSectionName, "BIT_SOURCE"))
  {
    if (pConfig->getValue(sIniSectionName, "BIT_SOURCE", lString))
    {   
      if (lString == "STREAM_BUILDER")
      {   
				lHandle->mPacketSource = new CStreamBuilder(
						lHandle->mPropertiesReader, lHandle->mProtocolEngine);
				lHandle->mExtBitSource = new CPacketBuilder(
						lHandle->mPropertiesReader, lHandle->mPacketSource);
				lHandle->mBitSource = new CFrameBuilder(lHandle->mPropertiesReader,
						lHandle->mExtBitSource);
      }
	  else if (lString == "FRAME_BUILDER")
	  {
				lHandle->mPacketSource = new CDatagramBuilder(
						lHandle->mPropertiesReader, lHandle->mProtocolEngine);
				lHandle->mExtBitSource = new CPacketBuilder(
						lHandle->mPropertiesReader, lHandle->mPacketSource);
				lHandle->mBitSource = new CFrameBuilder(lHandle->mPropertiesReader,
						lHandle->mExtBitSource);
	  }
      else /* lString == "BYTE_BUILDER" || lString == "DEFAULT" */
      {   
        lHandle->mBitSource = new CByteBuilder(lHandle->mProtocolEngine);
      }   
    }   
  }

	/* ======================== EmbeddingEngine ======================== */
  if (pConfig->containsKey(sIniSectionName, "EMBEDDING_ENGINE"))
  {
    if (pConfig->getValue(sIniSectionName, "EMBEDDING_ENGINE", lString))
    {   
      if (lString == CE_NULL_CYPHER) {
//			lHandle->mEmbeddingEngine = new CCryptoEngine();
    	  lHandle->mEmbeddingEngine= new CNullCryptoEngine(pIoStreamInfo);
		}else if(lString == CE_AMPLITUDE_SCRAMBLING){
			lHandle->mEmbeddingEngine = new CAmplitudeScrambling(pIoStreamInfo);
		}else if(lString == CE_FREQUENCY_PERMUTATION){
			lHandle->mEmbeddingEngine = new CFrequencyScrambling(pIoStreamInfo);
		}else if (lString == CE_ADV_AMPLITUDE_SCRAMBLING) {
			lHandle->mEmbeddingEngine = new CAdvAmplitudeScrambling(pIoStreamInfo);
		} else if (lString == "LSB_RAND")	{
				lHandle->mEmbeddingEngine = new CRandEmbeddingEngine(
          lHandle->mBitSource);
			}
      else /* lString == "LSB" || lString == "DEFAULT" */
      {   
				printl(0, __FILE__, "Using the LSB Embedding Engine. \n");
				lHandle->mEmbeddingEngine = new CLSBEmbeddingEngine(
						lHandle->mBitSource);
      }   
    }   
  }

	// set PropertiesReader for EmbeddingEngine
	if (lHandle->mPropertiesReader->containsSection(lHandle->mEmbeddingEngine->getIniSectionName())) {
		lHandle->mEmbeddingEngine->setPropertiesReader(lHandle->mPropertiesReader);
	}

	if (lHandle->mProtocolEngine) {
		if (!lHandle->mProtocolEngine->open()) {
			printl(LL_ERR, __FILE__, "CreateCallHandle: Open protocol engine failed.");
		}
	}

	return lHandle;
}

void CEngineFactory::destroyCallHandle_(CStegCallHandle** pCallHandle)
{
	// FIXXXME: this is not thread-safe
	(*pCallHandle)->mEmbeddingEngine->shutdown();
	delete (*pCallHandle)->mEmbeddingEngine;
	delete (*pCallHandle)->mBitSource;
	delete (*pCallHandle)->mExtBitSource;
	delete (*pCallHandle)->mPacketSource;
	if ((*pCallHandle)->mProtocolEngine) {
		(*pCallHandle)->mProtocolEngine->close();
	}
	delete (*pCallHandle)->mProtocolEngine;

	if(!(*pCallHandle)->mUseExternPropertiesReader) {
		// no extern properties reader was used --> free the intern one
		delete (*pCallHandle)->mPropertiesReader;
	}
	delete (*pCallHandle);

	(*pCallHandle) = NULL;
}

